﻿using Grpc.Net.Client.Balancer;
using Microsoft.Extensions.Logging;
using Nacos.V2;
using Nacos.V2.Naming.Dtos;
using XYS.Core.Util;
using XYS.Service.Core;

/// <summary>
/// nacos 解析器
/// </summary>
public class NacosResolver : Resolver
{
    private readonly Uri _address;
    private readonly INacosNamingService _nacosNamingService;
    private readonly ILogger _logger;
    private Action<ResolverResult> _listener;
    private Timer _refreshTimer;
    private CancellationTokenSource _cts;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="address"></param>
    /// <param name="nacosNamingService"></param>
    /// <param name="loggerFactory"></param>
    /// <exception cref="ArgumentNullException"></exception>
    public NacosResolver(Uri address, INacosNamingService nacosNamingService, ILoggerFactory loggerFactory)
    {
        _address = address ?? throw new ArgumentNullException(nameof(address));
        _nacosNamingService = nacosNamingService ?? throw new ArgumentNullException(nameof(nacosNamingService));
        _logger = loggerFactory?.CreateLogger<NacosResolver>();
        _cts = new CancellationTokenSource();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="listener"></param>
    public override void Start(Action<ResolverResult> listener)
    {
        _listener = listener;

        // 首次立即获取服务实例
        FetchAndUpdateInstancesAsync().DoNotAwait();

        // 启动定时刷新
        _refreshTimer = new Timer(
            _ => _ = FetchAndUpdateInstancesAsync(),
            null,
            TimeSpan.FromSeconds(30),
            TimeSpan.FromSeconds(30)
        );

        // 监听 Nacos 服务实例变更事件（可选）
        SubscribeToNacosEvents();
    }


    private async Task FetchAndUpdateInstancesAsync()
    {
        try
        {
            // 从 Nacos 获取指定服务的健康实例
            var instances = await _nacosNamingService.GetAllInstances(
                serviceName: _address.Host, // 服务名从 URI 的 Host 部分解析
                groupName: "DEFAULT_GROUP"
            );

            instances = instances.Where(i => i.Enabled == true && i.Healthy == true && i.Weight > 0).ToList();
            instances = instances.Where(e => !e.Metadata.TryGetValue(NacosMetadataKeys.Protocol, out var protocol) || protocol == NacosMetadataKeys.Grpc).ToList();

            List<BalancerAddress> addresses = new();
            foreach (var item in instances)
            {
                string host = item.Ip;
                int port = item.Port;
                //此处为了兼容java项目，无法注册多个nacos实例的情况，会将grpc端口号写在元数据中
                if (item.Metadata.TryGetValue("grpc.port", out var grpcPortString) && int.TryParse(grpcPortString, out var grpcPort))
                    port = grpcPort;
                //如果元数据中没有 NacosMetadataKeys.Protocol 标识，  则为历史实例， 兼容方式为 http端口 偏移1位。
                else if (!item.Metadata.ContainsKey(NacosMetadataKeys.Protocol))
                    port++;

                addresses.Add(new BalancerAddress(host, port));
            }
            // 通知 gRPC 客户端更新实例列表
            _listener(ResolverResult.ForResult(addresses));
        }
        catch (Exception ex)
        {
            _logger?.LogError(ex, "Failed to fetch instances from Nacos");
        }
    }

    private void SubscribeToNacosEvents()
    {
        // 订阅 Nacos 服务实例变更事件（SDK 需支持）
        _nacosNamingService.Subscribe(
            serviceName: _address.Host,
            groupName: "DEFAULT_GROUP",
            new EventListener(() => _ = FetchAndUpdateInstancesAsync())
        );
    }

    /// <summary>
    /// 释放
    /// </summary>
    /// <param name="disposing"></param>
    protected override void Dispose(bool disposing)
    {
        _refreshTimer?.Dispose();
        _cts.Cancel();
        base.Dispose(disposing);
    }


    private class EventListener : IEventListener
    {
        private readonly Action _onChange;

        public EventListener(Action onChange)
        {
            _onChange = onChange;
        }


        Task IEventListener.OnEvent(IEvent @event)
        {
            if (@event is Nacos.V2.Naming.Event.InstancesChangeEvent)
            {
                _onChange?.Invoke();
            }
            return Task.CompletedTask;
        }
    }
}